home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Pascal / Applications / NIH Image 1.60 / 1.60 Source / Dicom.p < prev    next >
Encoding:
Text File  |  1996-03-01  |  20.2 KB  |  784 lines  |  [TEXT/MWPS]

  1. {
  2. Dicom.p
  3.   by: Jim Nash, Synergistic Research Systems (jim.nash@his.com)
  4.   Reads and decodes the DICOM header so that NIH Image can
  5.   import DICOM images. DICOM (Digital Imaging and Communications
  6.   in Medicine) is a format popular in the medical imaging
  7.   community. This code is in the public domain.
  8. }
  9.  
  10.  
  11. unit DICOM;
  12.  
  13. interface
  14.  
  15.     uses
  16.         Types, Memory, QuickDraw, Packages, Menus, Events, Fonts, Scrap, ToolUtils, Resources, 
  17.         Errors, Palettes, Printing, StandardFile, Folders, TextUtils, Files,
  18.         globals, Utilities, Text, Graphics, Utilities, file2;
  19.  
  20.  
  21.     procedure ImportDICOMImages (fname: Str255; RefNum: integer; ImportAll: boolean; {}
  22.                                     function Import16BitFile (fname: str255; vnum: integer): boolean);
  23.  
  24. implementation
  25.  
  26.     const
  27.         dDicomName = 'DICOM dictionary';
  28.         maxElements = 1000;
  29.         elemNameLength = 50;
  30.     type
  31.         DataKind = (kUnknown, kString, kInteger, kLongint, kReal, kUInteger, kULongint);
  32.  
  33.         DataElement = record
  34.                 group, element: integer;
  35.                 code: packed array[1..2] of char;
  36.                 list: boolean;
  37.                 name: string[elemNameLength];
  38.             end;
  39.         ElemArray = array[1..maxElements] of DataElement;
  40.         ElemArrayPtr = ^ElemArray;
  41.  
  42.         DataDictionary = record
  43.                 number: integer;
  44.                 elem: ElemArrayPtr;
  45.             end;
  46.         DataDictionaryPtr = ^DataDictionary;
  47.     var
  48.         dictionary: DataDictionaryPtr;
  49.         loaded: boolean;
  50.         mySliceSpacing: real;
  51.  
  52.  
  53. { **************  Utility routines ***************** }
  54.  
  55.     procedure StringToBase (s: Str255; base: integer; var value: longint);
  56. {converts a string in some base to longint.  Typically}
  57. {base = 2,8,10,16 to represent binary, octal, decimal and hexadecimal}
  58.         var
  59.             ch: char;
  60.             good: boolean;
  61.             len, digit: integer;
  62.             i: longint;
  63.     begin
  64.         i := 1;
  65.         value := 0;
  66.         len := length(s);
  67.         while (i <= len) do begin
  68.             good := true;
  69.             ch := s[i];
  70.             if ch in ['A'..'Z'] then
  71.                 digit := ord(ch) - ord('A') + 10
  72.             else if ch in ['a'..'z'] then
  73.                 digit := ord(ch) - ord('a') + 10
  74.             else if ch in ['0'..'9'] then
  75.                 digit := ord(ch) - ord('0')
  76.             else
  77.                 good := false;
  78.             if good then
  79.                 value := value * base + digit;
  80.             i := i + 1;
  81.         end;
  82.     end;
  83.  
  84.  
  85.     procedure BaseToString (value: longint; base: integer; var s: Str255);
  86. {converts a long integer to a string in any base.  Typically}
  87. {base = 2,8,10,16 to represent binary, octal, decimal and hexadecimal.}
  88. {Ignores the sign bit unless base=10.}
  89.         var
  90.             sign, decimal: boolean;
  91.             digit: integer;
  92.             ch: char;
  93.     begin
  94.         decimal := (base = 10);
  95.         s := '';
  96.         sign := (value < 0);
  97.         if decimal then
  98.             value := abs(value)
  99.         else
  100.             value := BAND(value, $7FFFFFFF);
  101.         if value = 0 then
  102.             s := '0'
  103.         else
  104.             while (value <> 0) do begin
  105.                 digit := value mod base;
  106.                 value := value div base;
  107.                 if (digit >= 0) and (digit <= 9) then
  108.                     ch := chr(digit + ord('0'))
  109.                 else
  110.                     ch := chr(digit - 10 + ord('A'));
  111.                 s := concat(ch, s);
  112.             end;
  113.         if sign then
  114.             if decimal then
  115.                 s := concat('-', s)
  116.             else if s[1] < '2' then
  117.                 s[1] := chr(ord(s[1]) + 8)
  118.             else
  119.                 s[1] := chr(ord(s[1]) - ord('2') + ord('A'));
  120.     end;
  121.  
  122.  
  123.     function htos (i: longint): Str255;
  124. {A convenience function to replace BaseToString (hexadecimal) }
  125.         var
  126.             s: Str255;
  127.     begin
  128.         BaseToString(i, 16, s);
  129.         htos := s;
  130.     end;
  131.  
  132.  
  133.     function itos (i: longint): Str255;
  134. {A convenience function to replace NumToString}
  135.         var
  136.             s: Str255;
  137.     begin
  138.         NumToString(i, s);
  139.         itos := s;
  140.     end;
  141.  
  142.  
  143. { **************  DICOM routines ***************** }
  144.  
  145.  
  146.     procedure InitDICOM;
  147.         var
  148.             err: integer;
  149.     begin
  150.         dictionary := nil;
  151.         loaded := false;
  152.         DicomInitialized:=true;
  153.     end;
  154.  
  155.  
  156.  
  157.     function FindDicomElement (group, element: integer): integer;
  158.         var
  159.             i, index: integer;
  160.     begin
  161.         index := 0;
  162.         if loaded and (dictionary <> nil) then
  163.             with dictionary^ do
  164.                 if (elem <> nil) then begin
  165.                     i := 1;
  166.                     while (group > elem^[i].group) and (i < number) do
  167.                         i := i + 1;
  168.                     if (i <= number) then
  169.                         while (element > elem^[i].element) and (group = elem^[i].group) and (i < number) do
  170.                             i := i + 1;
  171.                     if (i <= number) then
  172.                         if (element = elem^[i].element) and (group = elem^[i].group) then
  173.                             index := i;
  174.                 end;
  175.         FindDicomElement := index;
  176.     end;
  177.  
  178.  
  179.     procedure InitUserChoice;
  180. {selected elements to list in text window, short form.}
  181. {This a minimum set.  If a user wants more information, they do a full dump.}
  182.  
  183.         procedure Select (group, element: integer);
  184.             var
  185.                 index: integer;
  186.         begin
  187.             with dictionary^ do begin
  188.                 index := FindDicomElement(group, element);
  189.                 if index > 0 then
  190.                     elem^[index].list := true;
  191.             end;
  192.         end;
  193.  
  194.     begin
  195.         with dictionary^ do begin
  196.             Select($8, $20);
  197.             Select($8, $30);
  198.             Select($8, $60);
  199.             Select($8, $1030);
  200.             Select($8, $103E);
  201.             Select($8, $1070);
  202.  
  203.             Select($10, $10);
  204.             Select($10, $20);
  205.             Select($10, $21B0);
  206.  
  207.             Select($18, $10);
  208.             Select($18, $50);
  209.             Select($18, $88);
  210.  
  211.             Select($20, $10);
  212.             Select($20, $11);
  213.             Select($20, $12);
  214.             Select($20, $13);
  215.  
  216.             Select($28, $10);
  217.             Select($28, $11);
  218.             Select($28, $30);
  219.             Select($28, $100);
  220.         end;
  221.     end;
  222.  
  223.  
  224.     procedure LoadDataDictionary;
  225.         type
  226.             CharBuf = packed array[0..100000] of char;
  227.             CharBufPtr = ^CharBuf;
  228.         var
  229.             err, refnum, len, i1, i2, n: integer;
  230.             index1, index2, logEOF, count, num, theSize: longint;
  231.             f: text;
  232.             sp: StringPtr;
  233.             str: Str255;
  234.             s1: Str255;
  235.             buf: CharBufPtr;
  236.  
  237.     begin
  238.         if dictionary = nil then begin
  239.             dictionary := DataDictionaryPtr(NewPtr(sizeof(DataDictionary)));
  240.             if dictionary <> nil then
  241.                 dictionary^.elem := ElemArrayPtr(NewPtr(sizeof(ElemArray)));
  242.             loaded := false;
  243.         end;
  244.         if (not loaded) and (dictionary <> nil) then
  245.             with dictionary^ do begin
  246.                 err := HSetVol(nil, StartupSpec.vRefNum, StartupSpec.parID);
  247.                 err := FSOpen(dDicomName, 0, refnum);     {check that file is present}
  248.                 if (err = 0) and (elem <> nil) then begin
  249.                     err := GetEOF(refnum, logEOF);
  250.                     buf := CharBufPtr(NewPtr(logEOF + 10));
  251.                     if (buf <> nil) then begin
  252.                         loaded := true;
  253.                         number := 0;
  254.                         count := logEOF;
  255.                         err := FSRead(refnum, count, ptr(buf));
  256.                         err := FSClose(refnum);
  257.                         index1 := 0;
  258.                         repeat
  259.                             index2 := index1;
  260.                             str := '';
  261.                             while (buf^[index2] <> cr) and (index2 < logEOF) and (length(str) < 255) do begin
  262.                                 str := concat(str, buf^[index2]);
  263.                                 index2 := index2 + 1;
  264.                             end;
  265.                             index1 := index2 + 1;
  266.                             len := length(str);
  267.                             if len > 0 then
  268.                                 if str[1] = '{' then begin
  269.                                     number := number + 1;
  270.                                     if (number mod 10) = 0 then
  271.                                         ShowAnimatedWatch;
  272.                                     with elem^[number] do begin
  273.                                         list := false;
  274.  
  275.                                         i1 := pos('x', str);
  276.                                         s1 := copy(str, i1 + 1, 4);
  277.                                         StringToBase(s1, 16, num);
  278.                                         group := num;
  279.                                         str := copy(str, i1 + 6, length(str)-(i1 + 6));
  280.  
  281.                                         i1 := pos('x', str);
  282.                                         s1 := copy(str, i1 + 1, 4);
  283.                                         StringToBase(s1, 16, num);
  284.                                         element := num;
  285.                                         str := copy(str, i1 + 6, length(str)-(i1 + 6));
  286.  
  287.                                         i1 := pos('''', str);
  288.                                         if length(str) >= (i1 + 2) then begin
  289.                                             code[1] := str[i1 + 1];
  290.                                             code[2] := str[i1 + 2];
  291.                                             str := copy(str, i1 + 5, length(str)-(i1 + 5));
  292.                                         end
  293.                                         else
  294.                                             str := '';
  295.  
  296.                                         i1 := pos('"', str);
  297.                                         if i1 > 0 then
  298.                                             str[i1] := ' ';
  299.                                         i2 := pos('"', str);
  300.                                         if i2 = 0 then
  301.                                             number := number - 1
  302.                                         else begin
  303.                                             n := i2 - i1 - 1;
  304.                                             if n > elemNameLength then
  305.                                                 n := elemNameLength;
  306.                                             name := copy(str, i1 + 1, n);
  307.                                         end;
  308.                                     end;
  309.                                 end;
  310.                         until (index1 >= logEOF);
  311.                     end;
  312.                     DisposePtr(ptr(buf));
  313.                 end;
  314.                 InitUserChoice;
  315.             end;
  316.     end;
  317. {$R+}
  318.  
  319.  
  320.     function GetDataKind (index: integer): DataKind;
  321.         var
  322.             kind: DataKind;
  323.     begin
  324.         kind := kUnknown;
  325.         if (dictionary <> nil) and (index > 0) then
  326.             with dictionary^.elem^[index] do begin
  327.                 if (code = 'AE') or (code = 'AS') or (code = 'CS') or (code = 'DA') or (code = 'DS') then
  328.                     kind := kString
  329.                 else if (code = 'DT') or (code = 'IS') or (code = 'LO') or (code = 'LT') or (code = 'PN') then
  330.                     kind := kString
  331.                 else if (code = 'SH') or (code = 'ST') or (code = 'TM') or (code = 'UI') then
  332.                     kind := kString
  333.                 else if (code = 'SS') then
  334.                     kind := kInteger
  335.                 else if (code = 'SL') then
  336.                     kind := kLongint
  337.                 else if (code = 'US') then
  338.                     kind := kUInteger
  339.                 else if (code = 'UL') then
  340.                     kind := kULongint;
  341.             end;
  342.         GetDataKind := kind;
  343.     end;
  344.  
  345.     procedure ImportDICOMImages (fname: Str255; RefNum: integer; ImportAll: boolean; {}
  346.                                     function Import16BitFile (fname: str255; vnum: integer): boolean);
  347.         var
  348.             enable_text, enable_open_text, first_image, sw, listAll, UseFixedScale: boolean;
  349.             ImageNumber:integer;
  350.             myIntercept, myScale: extended;
  351.  
  352.         function GetDICOMParams (fname: Str255; vNum: integer): integer;
  353.             const
  354.                 id_offset = 128;            {location of "DICM"}
  355.                 firstDicomElement = 132;    {first element}
  356.                 maxbuf = 20000;
  357.             type
  358.                 name4 = packed array[1..4] of char;
  359.                 name4ptr = ^name4;
  360.             var
  361.                 open_sw, done, window_sw: boolean;
  362.                 f, err, index, len: integer;
  363.                 groupWord, elementWord, lastGroup, FirstElement: integer;
  364.                 height, width, bits_alloc, bits_stored, high_bit, representation, offset: integer;
  365.                 amin, amax: integer;                {intensity range}
  366.                 scale, aspect, units: Str255;        {spatial}
  367.                 s, imgNumString, sliceSpacingStg, rescaleInterceptStg, rescaleSlopeStg: Str255;
  368.                 buflen, elementLength: longint;
  369.                 buf: packed array[0..maxbuf] of byte;
  370.                 kind: DataKind;
  371.                 dictionaryIndex: integer;
  372.                 xStr,yStr:str255;
  373.  
  374.             procedure MyWriteElement (str: Str255);
  375.                 const
  376.                     spaces = '                                                                                               ';
  377.                     padWidth = 4;
  378.                     nameWidth = 32;
  379.                 var
  380.                     s1, s2: str255;
  381.  
  382.                 function pad (s: Str255): Str255;
  383.                     const
  384.                         width = 4;
  385.                 begin
  386.                     while length(s) < width do
  387.                         s := concat(' ', s);
  388.                     pad := s;
  389.                 end;
  390.  
  391.             begin
  392.                 with dictionary^.elem^[dictionaryIndex] do begin
  393.                     s2 := name;
  394.                     if listAll then begin
  395.                         s1 := concat('(', pad(htos(groupWord)), ',', pad(htos(elementWord)), ')  (', pad(itos(elementLength)), ')');
  396.                         s2 := copy(concat(name, spaces), 1, nameWidth);
  397.                         s2 := concat(s1, '  ', code[1], code[2], '  ', s2);
  398.                     end;
  399.                     str := concat(s2, ':  ', str);
  400.                     if enable_text then begin
  401.                         if groupWord <> lastGroup then
  402.                             InsertText('', true);
  403.                         lastGroup := groupWord;
  404.                         InsertText(str, true);
  405.                     end;
  406.                 end;
  407.             end;
  408.  
  409.             function GetInteger (index: integer): integer;
  410.                 var
  411.                     i: integer;
  412.             begin
  413.                 i := buf[index] + $100 * buf[index + 1];
  414.                 GetInteger := i;
  415.             end;
  416.  
  417.             function GetLongint (index: integer): longint;
  418.                 var
  419.                     i: longint;
  420.             begin
  421.                 i := Ord4(buf[index]) + $100 * (buf[index + 1] + $100 * (buf[index + 2] + $100 * buf[index + 3]));
  422.                 GetLongint := i;
  423.             end;
  424.  
  425.             function GetUInteger (index: integer): longint;
  426.                 var
  427.                     i: integer;
  428.             begin
  429.                 i := buf[index] + $100 * buf[index + 1];
  430.                 GetUInteger := BAND(i, $FFFF);
  431.             end;
  432.  
  433.             function GetULongint (index: integer): longint;
  434.             {does not correctly report numbers > $7FFFFFFF}
  435.             begin
  436.                 GetULongint := GetLongint(index);
  437.             end;
  438.  
  439.             function GetString (index: integer): Str255;
  440.                 var
  441.                     i: integer;
  442.                     s: Str255;
  443.             begin
  444.                 s := '';
  445.                 for i := 0 to elementLength - 1 do
  446.                     s := concat(s, chr(buf[index + i]));
  447.                 GetString := s;
  448.             end;
  449.  
  450.         
  451.             function htos2(i: LongInt): str255;
  452.             {Converts an integer to hex using a fixed field width of 6}
  453.             var
  454.                 s: str255;
  455.             begin
  456.                 s := htos(i);
  457.                 while length(s) < 6 do
  458.                     s := concat(' ', s);
  459.                 htos2 := s;
  460.             end;
  461.  
  462.  
  463.             procedure DoByteSwap (var i: LongInt);
  464.                 var
  465.                     a: ostype;
  466.                     c: char;
  467.             begin
  468.                 a := ostype(i);
  469.                 c := a[1];
  470.                 a[1] := a[2];
  471.                 a[2] := c;
  472.                 c := a[3];
  473.                 a[3] := a[4];
  474.                 a[4] := c;
  475.                 i := LongInt(a)
  476.             end;
  477.             
  478.             
  479.     procedure Swap(var i: LongInt);
  480.         var
  481.             a: ostype;
  482.             c: char;
  483.     begin
  484.         a := ostype(i);
  485.         a[1] := a[3];
  486.         a[2] := a[4];
  487.         a[3] := chr(0);
  488.         a[4] := chr(0);
  489.         i := LongInt(a)
  490.     end;
  491.  
  492.  
  493.             procedure GetNextElement;
  494.                 var
  495.                     i: longint;
  496.                     str: Str255;
  497.             begin
  498.                 if index = 0 then
  499.                     index := firstElement
  500.                 else
  501.                     index := index + elementLength;
  502.                 if (index < 0) or (index >= buflen) then
  503.                     exit(GetNextElement);
  504.                 groupWord := GetInteger(index);
  505. {Swap2Bytes(groupWord);}
  506.                 elementWord := GetInteger(index + 2);
  507. {Swap2Bytes(elementWord);}
  508.                 elementLength := GetLongint(index + 4);
  509. {Swap4Bytes(elementLength);}
  510.                 if ControlKeyDown then
  511.                     InsertText(stringOf(index:6, htos2(groupWord), htos2(elementWord), htos2(elementLength)), true);                index := index + 8;
  512.                 dictionaryIndex := FindDicomElement(groupWord, elementWord);
  513.                 if dictionaryIndex > 0 then
  514.                     with dictionary^ do
  515.                         if elem^[dictionaryIndex].list or listAll then
  516.                             with elem^[dictionaryIndex] do begin
  517.                                 kind := GetDataKind(dictionaryIndex);
  518.                                 case kind of
  519.                                     kString: 
  520.                                         str := GetString(index);
  521.                                     kInteger: 
  522.                                         str := itos(GetInteger(index));
  523.                                     kLongint: 
  524.                                         str := itos(GetLongint(index));
  525.                                     kUInteger: 
  526.                                         str := itos(GetLongint(index));
  527.                                     kULongint: 
  528.                                         str := itos(GetULongint(index));
  529.                                     otherwise
  530.                                         str := 'unknown format';
  531.                                 end;
  532.                                 MyWriteElement(str);
  533.                             end;
  534.             end;
  535.  
  536.             function IsElement (group, element: integer): boolean;
  537.             begin
  538.                 IsElement := (group = groupWord) and (element = elementWord);
  539.             end;
  540.  
  541.             procedure TestError (err1: integer; str: Str255);
  542.                 var
  543.                     str1: Str255;
  544.             begin
  545.                 err := err1;
  546.                 if err1 <> 0 then begin
  547.                     if err <> 1 then
  548.                         str := concat(str, ' - error ', itos(err));
  549.                     PutMessage(str);
  550.                     if open_sw then
  551.                         err1 := fsclose(f);
  552.                     GetDICOMParams := err;
  553.                     exit(GetDICOMParams);
  554.                 end;
  555.             end;
  556.  
  557.             procedure OpenDicomTextWindow;
  558.                 var
  559.                     width, height: integer;
  560.             begin
  561.                 if listAll then begin
  562.                     width := 500;
  563.                     height := 400;
  564.                 end
  565.                 else begin
  566.                     width := 350;
  567.                     height := 300;
  568.                 end;
  569.                 if enable_open_text then
  570.                     window_sw := MakeNewTextWindow(concat(fname, ' header'), width, height);
  571.                 CurrentFontID := monaco;
  572.                 CurrentSize := 9;
  573.                 ChangeFontOrSize;
  574.                 enable_open_text := false;
  575.                 if enable_text then begin
  576.                     if loaded then
  577.                         InsertText('Selected fields from the DICOM file header', true)
  578.                     else begin
  579.                         InsertText(concat('Can''t find file: ', dDicomName, '.'), true);
  580.                         InsertText('', true);
  581.                         InsertText('This file is required to decode the DICOM header. It is available from: ftp://zippy.nimh.nih.gov/pub/nih-image/documents/dicom-dict.hqx. It must be located in the same folder as NIH Image or in the System folder.', true)
  582.                     end;
  583.                     InsertText('', true);
  584.                 end;
  585.             end;
  586.  
  587.         begin
  588.             err := 0;
  589.             buflen := maxbuf + 1;
  590.             open_sw := false;
  591.             TestError(fsopen(fname, vNum, f), 'Open');
  592.             open_sw := true;
  593.             TestError(FSRead(f, buflen, @buf), 'Read');
  594.             TestError(fsclose(f), 'Close');
  595.             if name4ptr(longint(@buf) + id_offset)^ <> 'DICM' then
  596.                 FirstElement:=0 {TestError(1, 'This is not a DICOM file.');}
  597.             else
  598.                 FirstElement:=FirstDicomElement;
  599.             OpenDicomTextWindow;
  600.             sliceSpacingStg := '1.0';
  601.             rescaleInterceptStg := '0.0';
  602.             rescaleSlopeStg := '1.0';
  603.             imgNumString := '';
  604.             height := -1;
  605.             width := -1;
  606.             offset := -1;
  607.             index := 0;
  608.             lastGroup := $8;
  609.             done := false;
  610.             scale := '0.0';
  611.             aspect := '1.0';
  612.             representation := 0;
  613.             amin := 0;
  614.             amax := 0;
  615.             units := '';
  616.             repeat
  617.                 GetNextElement;
  618.                 if (index < 0) or (index >= buflen) then
  619.                     leave;
  620.                 if IsElement($18, $88) then
  621.                     sliceSpacingStg := GetString(index)
  622.                 else if IsElement($20, $13) then
  623.                     imgNumString := GetString(index)
  624.                 else if IsElement($28, $10) then
  625.                     height := GetInteger(index)
  626.                 else if IsElement($28, $11) then
  627.                     width := GetInteger(index)
  628.                 else if IsElement($28, $30) then
  629.                     scale := GetString(index)
  630.                 else if IsElement($28, $34) then
  631.                     aspect := GetString(index)
  632.                 else if IsElement($28, $103) then
  633.                     representation := GetInteger(index)
  634.                 else if IsElement($28, $108) then
  635.                     amin := GetInteger(index)
  636.                 else if IsElement($28, $109) then
  637.                     amax := GetInteger(index)
  638.                 else if IsElement($28, $1052) then
  639.                     rescaleInterceptStg := GetString(index)
  640.                 else if IsElement($28, $1053) then
  641.                     rescaleSlopeStg := GetString(index)
  642.                 else if IsElement($7FE0, $10) then begin
  643.                     offset := index;
  644.                     done := true;
  645.                 end;
  646.                 if CommandPeriod then
  647.                     listAll := false;
  648.             until done;
  649.             if (width = -1) or (height = -1) or (offset = -1) then begin
  650.                 InsertText('Rows, columns or pixel data code ($7FE0, $10) was missing from DICOM header: can''t import file.', true);
  651.                 GetDICOMParams := -1;
  652.                 exit(GetDICOMParams)
  653.             end;
  654.  
  655.             {Image dimension information}
  656.             ImportCustomWidth := width;
  657.             ImportCustomHeight := height;
  658.             ImportCustomOffset := offset;
  659.             ImportSwapBytes := true; {(representation = 1);}
  660.  
  661.             {Intensity information}
  662.             ImportCustomDepth := SixteenBitsSigned;
  663.             if not ((amin = 0) and (amax = 0)) then begin
  664.                 ImportAutoScale:=false;
  665.                 ImportMin:=amin;
  666.                 ImportMax:=amax;
  667.             end else if (ImageNumber>1) and UseFixedScale then begin
  668.                 ImportAutoScale:=false;
  669.                 ImportMin:=info^.CurrentMin;
  670.                 ImportMax:=info^.CurrentMax;
  671.             end else
  672.                 ImportAutoScale:=true;;
  673.  
  674.             {convert from scaled units to independent units}
  675.             myIntercept := StringToReal(rescaleInterceptStg);
  676.             myScale := StringToReal(rescaleSlopeStg);
  677.  
  678.             {Spatial scale information}
  679.             with Info^ do begin
  680.                 PixelAspectRatio := StringToReal(aspect);
  681.                 xScale := 1;
  682.                 yScale := 1;
  683.                 zScale := 1.0 / StringToReal(sliceSpacingStg);
  684.  
  685.                 xUnit := '';
  686.                 SpatiallyCalibrated := false;
  687.                 if scale <> '' then begin
  688.                     xStr:=copy(scale, pos('\', scale) + 1, length(scale) - pos('\', scale));
  689.                     xScale := StringToReal(xStr);
  690.                     yStr:=copy(scale, 1, pos('\', scale) - 1);
  691.                     yScale := StringToReal(yStr);
  692.                     xUnit := 'mm';
  693.                     SpatiallyCalibrated := (xScale <> 0.0) and (yScale <> 0.0);
  694.                     if SpatiallyCalibrated then begin
  695.                         xScale := 1.0 / xScale;
  696.                         yScale := 1.0 / yScale;
  697.                     end;
  698.                 end;
  699.             end; {with}
  700.             if ImageNumber=1 then
  701.                 InsertText('', true);
  702.             GetDICOMParams := err;
  703.         end;
  704.  
  705.  
  706.         procedure UpdateCoefficients;
  707.         {Scale coefficients given Dicom Rescale Intercept and Rescale Slope}
  708.         begin
  709.             with info^ do begin
  710.                 info^.Coefficient[1] := myIntercept + myScale * info^.Coefficient[1];
  711.                 info^.Coefficient[2] := myScale * info^.Coefficient[2];
  712.                 fit := StraightLine;
  713.                 GenerateValues;
  714.             end;
  715.         end;
  716.         
  717.  
  718.         procedure ImportAllDicomFiles (RefNum: integer);
  719.             var
  720.                 OpenedOK: boolean;
  721.                 index: integer;
  722.                 name: Str255;
  723.                 ftype: OSType;
  724.                 err: OSErr;
  725.                 PB: HParamBlockRec;
  726.         begin
  727.             index := 0;
  728.             while true do begin
  729.                 index := index + 1;
  730.                 with PB do begin
  731.                     ioCompletion := nil;
  732.                     ioNamePtr := @name;
  733.                     ioVRefNum := RefNum;
  734.                     ioVersNum := 0;
  735.                     ioFDirIndex := index;
  736.                     err := PBGetFInfoSync(@PB);
  737.                     if err = fnfErr then
  738.                         exit(ImportAllDicomFiles);
  739.                     ftype := ioFlFndrInfo.fdType;
  740.                 end;
  741.  
  742. {AddDicomDumpName(fname);}
  743.                 if GetDICOMParams(name, RefNum) <> 0 then
  744.                     exit(ImportAllDicomFiles);
  745.                 if not Import16BitFile(name, RefNum) then
  746.                     exit(ImportAllDicomFiles);
  747.                 if (myIntercept <> 0.0) or (myScale <> 1.0) then
  748.                     UpdateCoefficients;
  749.                 with info^ do InsertText(StringOf(ImageNumber:3, ': "', title, '", min=', CurrentMin:1, ', max=', CurrentMax:1), true);
  750.                 ImageNumber:=ImageNumber+1;
  751.                 enable_text := false;        {text saved for first image only}
  752.                 first_image := false;
  753.                 if CommandPeriod then begin
  754.                     beep;
  755.                     exit(ImportAllDicomFiles);
  756.                 end;
  757.             end;
  758.         end;
  759.  
  760.     begin        {ImportDICOMImages}
  761.         if not DicomInitialized then
  762.             InitDICOM;
  763.         listAll := OptionKeyDown or OptionKeyWasDown;
  764.         enable_open_text := true;
  765.         enable_text := true;
  766.         first_image := true;
  767.         ImageNumber:=1;
  768.         UseFixedScale:=ShiftKeyDown;
  769.         LoadDataDictionary;
  770.         ImportingDicom := true;
  771.         if ImportAll then
  772.             ImportAllDicomFiles(RefNum)
  773.         else begin
  774.             if GetDICOMParams(fname, RefNum) <> 0 then
  775.                 exit(ImportDICOMImages);
  776.             if Import16BitFile(fname, RefNum) then
  777.                 if (myIntercept <> 0.0) or (myScale <> 1.0) then
  778.                     UpdateCoefficients;
  779.             with info^ do InsertText(StringOf('file "', title, '", min=', CurrentMin:1, ', max=', CurrentMax:1), true);
  780.         end;
  781.         ImportingDicom := false;
  782.     end;
  783.  
  784. end.